/*
   Copyright 2009 Abdulla Abdurakhmanov (abdulla@latestbit.com)
   Original sources are available at www.latestbit.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#include "cpputils/CppUtils.hpp"
#include "cpputils/utils/OSTools.hpp"

namespace cpputils {
	namespace utils {

		spawn_pid_t OSTools::runAsyncProcess(const char* sProcessPath, ...) {
			va_list aParamList;
			char* aProcessArgs[128] = { NULL };
			va_start(aParamList, sProcessPath);

			char* sArgument = NULL;
			int nIdx = 0;
			while ( (sArgument = va_arg(aParamList,char*)) != NULL)  {
				aProcessArgs[nIdx++] = sArgument;
			}
			spawn_pid_t nResult;
			#ifdef WIN32
				nResult = _spawnv ( _P_NOWAIT, sProcessPath, aProcessArgs );
				if (nResult == -1)
					throw SystemException("Unable to spawn process in asynch mode!");
			#else
				nResult = vfork();
				if(!nResult) {
					// Child process
					execv  ( sProcessPath, aProcessArgs );
					exit(0);
				}
				else {
					// Parent process
					if ( nResult == -1)
						throw SystemException("Unable to fork a child process in asynch mode!");
				}
			#endif
			va_end(aParamList);
			return nResult;
		}

		void OSTools::waitProcess(spawn_pid_t nProcessId) {
			#ifdef WIN32
				int nChildResult = 0;
				_cwait(&nChildResult, nProcessId, WAIT_CHILD);
			#else
				int nChildResult = 0;
				waitpid( nProcessId, &nChildResult, 0 );
			#endif
		}

		spawn_pid_t OSTools::getCurrentProcessId() {
			#ifdef WIN32
				return GetCurrentProcessId();
			#else
				return getpid();
			#endif
		}

		thread_id_t OSTools::getCurrentThreadId() {
			#ifdef WIN32
				return GetCurrentThreadId();
			#elif __APPLE__
				return MPCurrentTaskID();
			#else
				return pthread_self();
			#endif
		}

		bool OSTools::equalThreadId(thread_id_t hOne, thread_id_t hTwo) {
			#ifdef WIN32
				return hOne == hTwo;
			#elif __APPLE__
				return hOne == hTwo;
			#else
				return pthread_equal(hOne, hTwo) > 0;
			#endif
		}

		hrtime_t OSTools::gethrtime() {
			#ifdef WIN32
			  LARGE_INTEGER oFreq;
			  ::QueryPerformanceCounter (&oFreq);
			  return oFreq.QuadPart;
			#elif __APPLE__
			  	return mach_absolute_time();
			#else
				#if !defined(HAVE_THRTIME)
					#ifdef HAVE_CLOCK_GETTIME
					  struct timespec ts;
					  ::clock_gettime (CLOCK_REALTIME, &ts);
					  hrtime_t nTime = ts.tv_sec;
					  nTime *= NANOSECONDS_PER_SECOND;
					  nTime += ts.tv_nsec;
					  return nTime;
					#else
						#error "For this OS or environment) gethrtime haven't implementation"
					#endif
				#else
					return ::gethrtime();
				#endif
			#endif
		}

		hrtime_t OSTools::gethrfreq() {
			hrtime_t oHRFreq = 0;
			#ifdef WIN32
				LARGE_INTEGER oWinFreq;
				if(::QueryPerformanceFrequency(&oWinFreq))
					oHRFreq = oWinFreq.QuadPart / 1000;
				else
					oHRFreq = 1000;
			#else
				oHRFreq = 1000000;
			#endif
			return oHRFreq;
		}

		std::string OSTools::getCurrentLocale() {
			std::string result;
#ifdef WIN32
			CHAR pOutput[255]={0};
			GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, pOutput, 255 );
			result+=pOutput;
			result+="_";
			GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, pOutput, 255 );
			result+=pOutput;
#else
			char* pLocale = getenv("LC_ALL");
			if(!pLocale || !strcmp(pLocale, ""))
				pLocale = getenv("LANG");
			result = pLocale;
			boost::algorithm::trim(result);
			size_t nFindCodepage = result.find (".");

			if(nFindCodepage!=std::string::npos) {
				result = result.substr(0, nFindCodepage);
				boost::algorithm::trim(result);
			}
#endif
			return result;
		}
	}
}
